use core::slice;
use std::io::{Write, Read, Error as IOError, Seek, SeekFrom};
use super::bitsqueue::{BitsQueueU8, BitsQueueU64, BitsQueueU128};

pub struct BitsRead<'a, R: Read> {
    inner_byte_reader: &'a mut R,
    bitsqueue: BitsQueueU8,
    bitsqueue64: BitsQueueU64,
    bitsqueue128: BitsQueueU128
}

impl<'a, R: Read> BitsRead<'a, R> {
    pub fn create_with(reader: &'a mut R) -> Self {
        Self {
            inner_byte_reader: reader,
            bitsqueue: BitsQueueU8::new(),
            bitsqueue64: BitsQueueU64::new(),
            bitsqueue128: BitsQueueU128::new()
        }
    }

    pub fn read_bits_32_128_unchecked(&mut self, buf: &mut u32, nbits: u8) -> Result<bool, IOError> {
        let mut buf_16bytes = [0u8;16];

        *buf = 0u32;

        let mut remainning = nbits;

        while remainning > 0 {
            let n = remainning.min(self.bitsqueue128.size());

            if n == 0 {
                let read = self.inner_byte_reader.read(&mut buf_16bytes)?;
                if read == 0 {
                    return Ok(false);
                }

                self.bitsqueue128.fill_with_first_x_byte_of_16(read as u8, &buf_16bytes);
                continue;
            }

            let pulled = self.bitsqueue128.pull_unchecked(n) as u32;            

            *buf = if n == 32 { pulled } else { ((*buf) << n) | pulled };

            remainning -= n;
        }

        Ok(true)
    }
    
    pub fn read_bits_32_64_unchecked(&mut self, buf: &mut u32, nbits: u8) -> Result<bool, IOError> {
        let mut buf_8bytes = [0u8;8];

        *buf = 0u32;

        let mut remainning = nbits;

        while remainning > 0 {
            let n = remainning.min(self.bitsqueue64.size());

            if n == 0 {
                let read = self.inner_byte_reader.read(&mut buf_8bytes)?;
                if read == 0 {
                    return Ok(false);
                }

                self.bitsqueue64.fill_with_first_x_byte_of_8(read as u8, &buf_8bytes);
                continue;
            }

            let pulled = self.bitsqueue64.pull_unchecked(n) as u32;            

            *buf = if n == 32 { pulled } else { ((*buf) << n) | pulled };

            remainning -= n;
        }

        Ok(true)
    }

    pub fn read_bits_16_128_unchecked(&mut self, buf: &mut u16, nbits: u8) -> Result<bool, IOError> {
        let mut val = 0u32;
        let r = self.read_bits_32_128_unchecked(&mut val, nbits);
        if let Ok(_) = r {
            *buf = val as u16
        }
        r
    }

    pub fn read_bits_16_64_unchecked(&mut self, buf: &mut u16, nbits: u8) -> Result<bool, IOError> {
        let mut val = 0u32;
        let r = self.read_bits_32_64_unchecked(&mut val, nbits);
        if let Ok(_) = r {
            *buf = val as u16
        }
        r
    }

    pub fn read_bits_8_128_unchecked(&mut self, buf: &mut u8, nbits: u8) -> Result<bool, IOError> {
        let mut val = 0u32;
        let r = self.read_bits_32_128_unchecked(&mut val, nbits);
        if let Ok(_) = r {
            *buf = val as u8
        }
        r
    }

    pub fn read_bits_8_64_unchecked(&mut self, buf: &mut u8, nbits: u8) -> Result<bool, IOError> {
        let mut val = 0u32;
        let r = self.read_bits_32_64_unchecked(&mut val, nbits);
        if let Ok(_) = r {
            *buf = val as u8
        }
        r
    }

    pub fn read_bits_8_8_unchecked(&mut self, buf: &mut u8, nbits: u8) -> Result<bool, IOError> {
        let mut onebyte_buf = [0u8];

        if nbits == 8 && self.bitsqueue.size() == 0 {
            if self.inner_byte_reader.read(slice::from_mut(buf))? < 1 {
                return Ok(false);
            }
            return Ok(true);
        }

        let mut val = 0u8;
        let mut remainning = nbits;

        while remainning > 0 {
            let n = remainning.min(self.bitsqueue.size());

            if n == 0 {
                if self.inner_byte_reader.read(&mut onebyte_buf)? < 1 {
                    return Ok(false);
                }
                self.bitsqueue.fill_with(onebyte_buf[0]);

                continue;
            }

            if n < 8 {
                val <<= n;
            }
            val |= self.bitsqueue.pull(n).unwrap();

            remainning -= n;
        }

        *buf = val;

        Ok(true)
    }

    pub fn clear_bits_buf(&mut self) {
        self.bitsqueue.clear();
    }
}
pub struct BitsWrite<'a, W: Write> {
    inner_byte_writer: &'a mut W,
    bitsqueue: BitsQueueU8,
}

impl<'a, W: Write> BitsWrite<'a, W> {
    pub fn create_with(writer: &'a mut W) -> Self {
        Self {
            inner_byte_writer: writer,
            bitsqueue: BitsQueueU8::new(),
        }
    }

    #[inline(always)]
    pub fn write_bytes(&mut self,  buf: &[u8]) -> Result<usize, IOError> {
        self.inner_byte_writer.write(buf)
    }

    pub fn write_bits_32_8_unchecked(&mut self, val: u32, nbits: u8) -> Result<(), IOError> {
        if nbits == 8 && self.bitsqueue.size() == 0 {
            self.inner_byte_writer.write_all(slice::from_ref(&(val as u8)))?;
            return Ok(());
        }

        for v in self.bitsqueue.fill_iter(val, nbits, true) {
            self.inner_byte_writer.write_all(slice::from_ref(&v))?;
        }

        Ok(())        
    }

    pub fn flush_buffered_bits(&mut self) -> Result<(), IOError> {
        if self.bitsqueue.size() > 0 {
            self.inner_byte_writer.write_all(slice::from_ref(&self.bitsqueue.val()))?;
            self.bitsqueue.clear();
        }
        Ok(())
    }
}

impl<'a, R: Read + Seek> BitsRead<'a, R> {
    pub fn seek_byte(&mut self, pos: SeekFrom) -> Result<u64, IOError> {
        self.inner_byte_reader.seek(pos)
    }
}

impl<'a, W: Write + Seek> BitsWrite<'a, W> {
    pub fn seek_byte(&mut self, pos: SeekFrom) -> Result<u64, IOError> {
        self.inner_byte_writer.seek(pos)
    }
}